package ch.zbw.sysventory.agent.socketserver;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

import org.apache.log4j.Logger;

import ch.zbw.sysventory.agent.socketserver.handlers.ClientRequestHandler;
import ch.zbw.sysventory.agent.socketserver.handlers.ClientRequestHandlerFactory;
import ch.zbw.sysventory.agent.utils.AgentPropertiesUtil;

public class AgentTCPServer extends Thread implements Runnable {

	private Logger logger = Logger.getRootLogger();

	private AgentPropertiesUtil propertiesUtil;
	private ServerSocket srvSocket;

	public AgentTCPServer(AgentPropertiesUtil propertiesUtil) {
		this.propertiesUtil = propertiesUtil;
	}

	@Override
	public void run() {
		String portString = this.propertiesUtil.getProperties().getProperty("sysventory.agent.port");
		int port = Integer.valueOf(portString);
		if (port > 0) {
			try {
				this.srvSocket = new ServerSocket(port);
				if (!srvSocket.isBound()) {
					logger.error("Cannot startup TCP server on this port: " + port);
				} else {
					logger.info("Started up TCP server on port " + port);
					while (!isInterrupted()) {
						Socket client = srvSocket.accept();
						logger.info("new client connection");
						DataOutputStream out = new DataOutputStream(client.getOutputStream());

						InputStream in = client.getInputStream();
						DataInputStream dis = new DataInputStream(in);
						long size = dis.readLong();
						byte[] buffer = new byte[1024];
						int bytesRead;
						ByteArrayOutputStream output = new ByteArrayOutputStream();
						while (size > 0 && (bytesRead = in.read(buffer, 0, (int) Math.min(buffer.length, size))) != -1) {
							output.write(buffer, 0, bytesRead);
							size -= bytesRead;
						}
						String message = new String(output.toByteArray());

						logger.info("Received message from client: " + message);
						ClientRequestHandler handler = ClientRequestHandlerFactory.getInstance(propertiesUtil).getRequestHandler(message);
						if (handler != null) {
							logger.info("Pass request to " + handler.getClass().getCanonicalName());
							handler.handleClientRequest(message, out);
						}
						in.close();
						out.close();
						client.close();
					}
				}
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}

		} else {
			logger.error("Cannot read port from property file!");
		}
	}

	@Override
	public void interrupt() {
		super.interrupt();
		if (this.srvSocket != null && !this.srvSocket.isClosed()) {
			try {
				this.srvSocket.close();
				logger.info("Server socket closed");
			} catch (IOException e) {
				logger.error("Cannot close server socket!", e);
			}
		}
	}
}
